package com.bandit.proxy;

import com.bandit.config.RpcServiceConfig;
import com.bandit.emums.RpcExceptionMessageEnum;
import com.bandit.emums.RpcResponseCodeEnum;
import com.bandit.exception.RpcException;
import com.bandit.remoting.dto.RpcRequest;
import com.bandit.remoting.dto.RpcResponse;
import com.bandit.remoting.transport.RpcRequestTransport;
import com.bandit.remoting.transport.netty.client.RpcNettyClient;
import com.bandit.remoting.transport.socket.RpcSocketClient;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * 动态代理
 * 动态代理对象调用一个方法时实际上调用Invoke方法
 * 需要动态代理来使客户机调用远程方法时屏蔽中间繁琐的过程
 * @author Bandit
 * @create 2022/2/23 17:22
 */
@Slf4j
public class RpcClientProxy implements InvocationHandler {

    private static final String INTERFACE_NAME = "interfaceName";

    private final RpcRequestTransport rpcRequestTransport;
    private final RpcServiceConfig rpcServiceConfig;

    public RpcClientProxy(RpcRequestTransport rpcRequestTransport, RpcServiceConfig rpcServiceConfig){
        this.rpcRequestTransport = rpcRequestTransport;
        this.rpcServiceConfig = rpcServiceConfig;
    }

    public RpcClientProxy(RpcRequestTransport rpcRequestTransport){
        this.rpcRequestTransport = rpcRequestTransport;
        this.rpcServiceConfig = new RpcServiceConfig();
    }

    /**
     * 获取代理对象
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},this);
    }

    /**
     * 用代理对象调用方法实际上会走invoke，
     * invoke代理去走被代理的方法
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("invoked method:[{}]",method.getName());
        RpcRequest rpcRequest = RpcRequest.builder()
                .methodName(method.getName())
                .parameters(args)
                .paramTypes(method.getParameterTypes())
                .interfaceName(method.getDeclaringClass().getName())
                .requestId(UUID.randomUUID().toString())
                .group(rpcServiceConfig.getGroup())
                .version(rpcServiceConfig.getVersion())
                .build();
        RpcResponse<Object> rpcResponse = null;
        //Socket方式实现
        if(rpcRequestTransport instanceof RpcSocketClient) {
            rpcResponse = (RpcResponse<Object>) rpcRequestTransport.sendRpcRequest(rpcRequest);
        }
        //Netty方式实现
        if(rpcRequestTransport instanceof RpcNettyClient) {
            CompletableFuture<RpcResponse<Object>> completableFuture = (CompletableFuture<RpcResponse<Object>>) rpcRequestTransport.sendRpcRequest(rpcRequest);
            rpcResponse = completableFuture.get();
        }
        check(rpcResponse, rpcRequest);
        return rpcResponse.getData();
    }

    /**
     * 检测是否出错
     * @param rpcResponse
     * @param rpcRequest
     */
    private void check(RpcResponse<Object> rpcResponse, RpcRequest rpcRequest){
        String reqDetails = INTERFACE_NAME + ":" + rpcRequest.getInterfaceName();
        if(rpcResponse == null) {
            throw new RpcException(RpcExceptionMessageEnum.SERVICE_INVOCATION_FAILURE, reqDetails);
        }

        if(!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) {
            throw new RpcException(RpcExceptionMessageEnum.REQUEST_NOT_MATCH_RESPONSE, reqDetails);
        }

        if (rpcResponse.getCode() == null || !rpcResponse.getCode().equals(RpcResponseCodeEnum.SUCCESS.getCode())) {
            throw new RpcException(RpcExceptionMessageEnum.SERVICE_INVOCATION_FAILURE, reqDetails);
        }
    }
}
